package com.rjj.websocket.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <a href='http://www.fly63.com/article/detial/6128'>websocket断开问题</a>
 *
 * @author renjianjun
 *
 * 4.创建websocket服务端处理类（核心），代码功能看注释
 *
 * 刷新浏览器会重新建立会话
 */
@Component
@ServerEndpoint("/mysocket/{ids}/{name}")
@Slf4j
public class Websocket {

    //保存名称，这里可以保存用户信息一直使用，但是我这里使用前端传值，所以暂时不用
    //private static final Map<String, Map> WEB_KV = new ConcurrentHashMap<>();
    /**每个客户的连接，在线用户会话,直接使用这个只能够拿到会话，不知道当前会话是哪个人的**/
    //private static CopyOnWriteArrayList<Websocket> websocketList = new CopyOnWriteArrayList<>();

    /**用来存放每个客户端对应的MyWebSocket对象，可以更具key获取哪个人的会话，这样后期方便做处指定人发送消息**/
    private final static Map<String,Websocket> WEBSOCKET_MAP = new ConcurrentHashMap<>();

    /**保存用户的名称**/
    static Map<String,String> nameMap = new ConcurrentHashMap<>();

    /** 与某个客户端的连接会话，需要通过它来给客户端发送数据 **/
    private Session session;
    private String ids;
    //获取名称
    private String getName(){
        return nameMap.get(ids);
    }

    /**
     * 连接成功调用
     **/
    @OnOpen
    public void onOpen(@PathParam("ids") String ids,@PathParam("name") String name, Session session){
        this.ids = ids;
        //上线保存当前会话,这样使用的时候就是当前会话了
        this.session = session;

        /**名称的唯一行保存**/
        nameMap.put(ids,name);
        //会话池增加会话数就是在线人数
        //websocketList.add(this);

        /** 这样子可以保证一个浏览器中只有一个会话，因为有这个ids **/
        WEBSOCKET_MAP.put(ids,this);

        this.sendMessageToAll("[系统消息]：" + name + " 上线了",true);
        sendOnLineCount();
    }


    /**
     * 连接关闭调用
     */
    @OnClose
    public void onClose(){
        //会话关闭，移除在线会话
        //websocketList.remove(this);
        //移除会话
        WEBSOCKET_MAP.remove(ids);

        this.sendMessageToAll("[系统消息]：" + getName() + " 下线了",true);
        sendOnLineCount();
    }

    /**
     * 收到客户端消息时候调用
     * @param message 收到的消息
     * @param session 当前的客户
     */
    @OnMessage
    public void onMessage(String message, Session session){
        log.info("=======" + getName());
        if("ping".equals(message)){
            //如果是心跳检测，那么只与自己通信
            sendMessage("pong");
        } else {
            //群发消息给客户
            sendMessageToAll(getName() + ":" + message,true);
        }
        log.info("发送到后台消息是：" + message);
    }

    /**
     * 发生错误时候调用
     */
    @OnError
    public void onError(Throwable error){
        log.info("发生错误");
        error.printStackTrace();
    }


    /**
     * 发消息
     * 多线程下，可能多个人会用到同一个session，导致出现问题
     */
    private void sendMessage(String message){
        synchronized (this.session){
            try {
                this.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发消息给所有人
     * 自己是不是也要收到 true 收到 false不收到
     */
    private void sendMessageToAll(String message, boolean isSelf){
       /* for (Websocket websocket : websocketList) {
            //只要一个是true那么都会执行，双false才会不执行
            if(isSelf || websocket != this){
                websocket.sendMessage(message);
            }
        }*/

        WEBSOCKET_MAP.forEach((s, websocket) -> {
            //只要一个是true那么都会执行，双false才会不执行
            if(isSelf || websocket != this){
                websocket.sendMessage(message);
            }
        });
    }

    /**
     * 指定人发消息
     */
    public void sendMessageToPeople(String ids,String message){
        Websocket websocket = WEBSOCKET_MAP.get(ids);
        if(null != websocket){
            websocket.sendMessage(message);
        }else {
            this.sendMessage("[系统消息]：" + getName() + "不在线！");
        }
    }

    /**
     * 发送在线人数通知
     */
    private void sendOnLineCount(){
        this.sendMessageToAll("在线人数：" + WEBSOCKET_MAP.size(), true);

    }
}
